﻿#ifndef _CELLBUFFER_H_
#define _CELLBUFFER_H_

#include "CELL.h"
#include <string.h>
#ifdef _WIN32
#include "CELLIOCP.hpp"
#endif

class CELLBuffer
{
public:
	CELLBuffer(int nSize = 8192) : _nSize(nSize)
	{
		_pBuff = new char[_nSize];
	}

	~CELLBuffer()
	{
		if (_pBuff)
		{
			delete[] _pBuff;
			_pBuff = nullptr;
		}
	}

	int write2socket(SOCKET sockfd)
	{
		int ret = 0;
		// 本客户端消息发送缓冲区中有数据并且本客户端套接字是有效的
		if (_nLast > 0 && INVALID_SOCKET != sockfd)
		{
			// 发送发送缓冲区的数据
			ret = send(sockfd, _pBuff, _nLast, 0);
			if (ret <= 0)
			{
				return SOCKET_ERROR;
			}
			else if (ret == _nLast)
			{
				// 将发送缓冲区的尾部指针置0
				_nLast = 0;
			}
			else
			{
				_nLast -= ret;
				memcpy(_pBuff, _pBuff + ret, _nLast);
			}

			// 
			_fullCount = 0;
		}
		// 返回0或者返回send函数的返回值
		return ret;
	}

	int read4socket(SOCKET sockfd)
	{
		if (_nSize - _nLast > 0)
		{
			// 还有空位置可以用
			// 接受客户端数据
			char* szRecv = _pBuff + _nLast;
			int nLen = (int)recv(sockfd, szRecv, _nSize - _nLast, 0);
			if (nLen <= 0)
			{
				CELLLog_Debug("CELLBuffer::read4socket, nLen = %d", nLen);
				return SOCKET_ERROR;
			}
			// 缓冲区尾部位置后移
			_nLast += nLen;
			return nLen;
		}
		return 0;
	}

	bool push(const char* pData, int nLen)
	{
		/*
		在这里添加数据的时候如果超过了，可以在这里扩展原空间
			或者写入数据库或磁盘，看需求
	*/
		if (_nLast + nLen <= _nSize)
		{
			// 将要发送的数据拷贝到发送缓冲区尾部
			memcpy(_pBuff + _nLast, pData, nLen);
			// 更新缓冲区长度
			_nLast += nLen;
			if (_nLast == SEND_BUF_SIZE)
			{
				// 发送缓冲区刚好满
				++_fullCount;
			}
			return true;
		}
		else
		{
			// 发送缓冲区已满(超出)
			++_fullCount;
		}

		return false;
	}

	void pop(int nLen)
	{
		int n = _nLast - nLen;
		// 判断删除的数据长度是正常的
		if (n > 0)
		{
			memcpy(_pBuff, _pBuff + nLen, n);
		}
		// 缓冲区尾部指针前移
		_nLast = n;
		if (_fullCount > 0)
			--_fullCount;
	}

	bool hasMsg()
	{
		if (_nLast >= sizeof(DataHeader))
		{
			// 强转类型就可以知道这次消息的长度了
			DataHeader* header = (DataHeader*)_pBuff;
			// 判断客户端消息缓冲区的数据长度是否大于消息长度
			return _nLast >= header->dataLength;
		}
		// 缓冲区数据不够一条消息长度
		return false;
	}

	bool needWrite()
	{
		// 当客户端应用时要判断是否要发送数据给服务端，只要缓冲区有数据就可以了
		return _nLast > 0;
	}

	char* data()
	{
		return _pBuff;
	}
	
	// 返回buffer总大小
	int buffSize()
	{
		return _nSize;
	}

	// 返回已有数据的长度
	int dataLen()
	{
		return _nLast;
	}

#ifdef CELL_USE_IOCP // 如果使用了IOCP模式
	/*
		对于IOCP来说，不是有了数据通知程序去接收或发送，是接收完毕/发送完毕才会通知程序，
		所以write2socket和read4socket对于IOCP模式就失去了意义，
		在这里是使用指针的方式来告诉IOCP将数据存放在缓冲区的哪个位置
	*/
	IO_DATA_BASE* makeRecvIOData(SOCKET sockfd)
	{
		int nLen = _nSize - _nLast;
		if (nLen > 0)
		{
			// 如果接收缓冲区还可以空余空间的话，就将本次io缓冲区中指针指向本次接收
			// 缓冲区数据位置，IOCP就会把数据放在这个位置上面
			_ioData.wsaBuff.buf = _pBuff + _nLast;
			_ioData.wsaBuff.len = nLen;
			_ioData.sockfd = sockfd;
			return &_ioData;
		}
		// 在缓冲区没有位置存放数据的时候返回空指针
		return nullptr;
	}

	// 数据对于IOCP来说是接收完成了告诉程序，那么就需要告诉缓冲区本次接收到了多少长度
	bool read4iocp(int nRecv)
	{
		if (nRecv > 0 && _nSize - _nLast >= nRecv)
		{
			// makeIOData的时候是判断了有没有剩余的空间去收数据的，
			// 告诉缓冲区本次接收了多少数据的时候还是判断了一下
			_nLast += nRecv;
			return true;
		}
		CELLLog_Error("CELLBuffer read4iocp:sockfd<%d> nSize<%d> nLast<%d> nRecv<%d>", _ioData.sockfd, _nSize, _nLast, nRecv);
		return false;
	}

	IO_DATA_BASE* makeSendIOData(SOCKET sockfd)
	{
		if (_nLast > 0)
		{
			// 如果接收缓冲区还可以空余空间的话，就将本次io缓冲区中指针指向本次接收
			// 缓冲区数据位置，IOCP就会把数据放在这个位置上面
			_ioData.wsaBuff.buf = _pBuff;
			_ioData.wsaBuff.len = _nLast;
			_ioData.sockfd = sockfd;
			return &_ioData;
		}
		// 在缓冲区没有数据要发送的时候返回空指针
		return nullptr;
	}

	bool write2iocp(int nSend)
	{
		if (_nLast < nSend)
		{
			CELLLog_Error("write2iocp:sockfd<%d> nSize<%d> nLast<%d> nSend<%d>", _ioData.sockfd, _nSize, _nLast, nSend);
			return false;
		}
		else if (_nLast == nSend)
		{//_nLast=2000 实际发送nSend=2000
		 //数据尾部位置清零
			_nLast = 0;
		}
		else {
			//_nLast=2000 实际发送ret=1000
			_nLast -= nSend;
			memcpy(_pBuff, _pBuff + nSend, _nLast);
		}

		_fullCount = 0;
		return true;
	}
#endif // #ifdef CELL_USE_IOCP
private:
	// 这里的缓冲区可以不用new的方式，也可以是链表或队列的一种形式
	// list<char*> _pBuffList;
	// 缓冲区指针
	char* _pBuff = nullptr;
	// 缓冲区的数据尾部位置(已有数据大小)
	int _nLast = 0;
	// 缓冲区总的空间大小(字节长度)
	int _nSize = 0;
	// 缓冲区写满计数
	int _fullCount = 0;
#ifdef CELL_USE_IOCP
	IO_DATA_BASE _ioData = { 0 };
#endif // #ifdef CELL_USE_IOCP
};

#endif // #ifndef _CELLBUFFER_H_
